/*
 *  Emotion Media Center
 *
 *  Copyright (C) 2009 Davide Andreoli <dave@gurumeditation.it>
 *
 *  License LGPL-2.1, see COPYING file at project folder.
 *
 */

#include "emc.h"
#include "emc_module.h"
#include "emc_input.h"
#include "emc_mainmenu.h"
#include "emc_browser_obj.h"
#include "emc_gui.h"
#include "emc_strbuf.h"
#include "emc_config.h"

#define EMC_MODULE_NAME "games_mame"

#define LOCAL_DEBUG 1
#if LOCAL_DEBUG
#define D DBG
#else
#define D(...)
#endif


typedef struct _Mame_Game Mame_Game;
struct _Mame_Game
{
   const char  *id;
   const char  *name;
};

static Eina_List           *mame_games = NULL;
static Eina_Hash           *mame_games_hash = NULL;
static Mame_Game           *mame_current_game = NULL;
static Ecore_Event_Handler *mame_exe_handler = NULL;
static Evas_Object         *mame_info_pager = NULL;

// protos TODO finishme
static void _update_info(Mame_Game *game);

/******************************************************************************/
/*                         MAME Frontend Stuff                                */
/******************************************************************************/

static int
_exe_exit(void *data, int ev_type, void *ev)
{
   // Ecore_Exe_Event_Del *e = ev;

   ecore_event_handler_del(mame_exe_handler);
   mame_exe_handler = NULL;

   emc_input_event_thaw();

   if (data) elm_win_fullscreen_set(emc->win, EINA_TRUE);
   
   return 1;
}

static void
_mame_run_game(const char *id)
{
   char cmd[128];
   
   D("%s", id);

   emc_input_event_freeze();

   snprintf(cmd, sizeof(cmd), "mame %s", id);
   ecore_exe_run(cmd, NULL);
   mame_exe_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _exe_exit,
                                              elm_win_fullscreen_get(emc->win));

   elm_win_fullscreen_set(emc->win, EINA_FALSE);
}

static int
_sort_cb(const void *d1, const void *d2)
{
   const Mame_Game *game1 = d1;
   const Mame_Game *game2 = d2;

   if(!game1 || !game1->name) return(1);
   if(!game2 || !game2->name) return(-1);

   return(strcmp(game1->name, game2->name));
}

static void
_mame_build_list(void)
{
   FILE *fp;
   char line[1024];

   D(" ");
   if (mame_games_hash) return;

   mame_games_hash = eina_hash_string_superfast_new(NULL); //TODO: or use stringshare hash?
   
   fp = popen("mame -listfull", "r");
   if (fp == NULL) return ;

   while (fgets(line, sizeof(line), fp) != NULL)
   {
      char id[64];
      char name[256];
      int res;

      res = sscanf(line, "%s \"%[^\"]", id, name);
      if (res == 2)
      {
         Mame_Game *game;

         // D("name[%s]: '%s'", _id, _name);
         // alloc the new game
         game = EMC_NEW(Mame_Game);
         game->id = eina_stringshare_add(id);
         game->name = eina_stringshare_add(name);

         // add the game to the list and to the hash
         mame_games = eina_list_append(mame_games, game);
         eina_hash_direct_add(mame_games_hash, game->id, game);
      }
   }
   pclose(fp);

   mame_games = eina_list_sort(mame_games, 0, _sort_cb);
}

Emc_Strbuf *
_mame_history_get(Mame_Game *game)
{
   FILE *fp;
   char line[PATH_MAX]; // TODO is this enough ??
   char to_search[128];
   Emc_Strbuf *strbuf;
   Eina_Bool found = EINA_FALSE;
   
   snprintf(to_search, sizeof(to_search), "%s,", game->id);
   D("SEARCHING FOR: '%s'", to_search);

   fp = fopen(emc_config_string_get("mame", "history_file"), "r");
   if (!fp)
   {
      strbuf = emc_strbuf_new();
      //~ emc_strbuf_append(strbuf, "ERROR");
      //~ D("AZZ[%s]", strbuf->buf);
      emc_strbuf_appendf(strbuf,
         "<b>Error:<br>Could not find the history file:</b><br>%s<br><br>"
         "You must download the file from the net or adjust the path to "
         "your history.dat file in the emc config file.",
         emc_config_string_get("mame", "history_file"));
      return strbuf;
   }

   // search the '$info=...' line
   while (fgets(line, PATH_MAX, fp) != NULL)
   {
      if (!strncmp(line, "$info=", 6) && strstr(line, to_search))
      {
            found = EINA_TRUE;
            break;
      }
   }
   if (!found)
   {
      strbuf = emc_strbuf_new();
      emc_strbuf_append(strbuf, "Error: No game info in the history file.");
      fclose(fp);
      return strbuf;
   }
   
   // search the '$bio' line
   while (fgets(line, PATH_MAX, fp) != NULL)
      if (!strncmp(line, "$bio", 4)) break;
   
   // get the data until the '$end' line
   strbuf = emc_strbuf_new();
   while (fgets(line, PATH_MAX, fp) != NULL)
   {
      char *markup;
      
      if (!strncmp(line, "$end", 4)) break;
      markup = elm_entry_utf8_to_markup(line);
      emc_strbuf_append(strbuf, markup);
      free(markup);
   }
   
   fclose(fp);
   
   return strbuf;
}

/*****************************************************************************/
/*                         detail panel                                      */
/*****************************************************************************/
static void
_dwnl_rom_complete_cb(void *data, const char *file, int status)
{
   Evas_Object *lb;
   
   D(" ");
   elm_pager_content_pop(mame_info_pager);

   lb = elm_label_add(emc->layout);
   elm_label_label_set(lb, "Download Complete");
   evas_object_show(lb);

   elm_pager_content_push(mame_info_pager, lb);
}

static int
_dwnl_rom_progress_cb(void *data, const char *file, long int dltotal, long int dlnow, long int ultotal, long int ulnow)
{
   Evas_Object *prog = data;

   elm_progressbar_value_set(prog, (double)dlnow / (double)dltotal);
   return 0;
}

static void
_download_clicked(void *data, Evas_Object *obj, void *event_info)
{
   Mame_Game *game = data;
   char url[PATH_MAX];
   char buf[PATH_MAX];
   Evas_Object *vbox, *prog, *o;
   int res;
   
   D(" ");
   elm_pager_content_pop(mame_info_pager);
   
   // http://roms.freeroms.com/mame_roms/NUM/88games.zip //TODO fix NUM
   // http://roms.freeroms.com/mame_roms/a/ad2083.zip
   snprintf(url, sizeof(url), "http://roms.freeroms.com/mame_roms/%c/%s.zip",
                 game->id[0], game->id);
   snprintf(buf, sizeof(buf), "%s/%s.zip",
                 emc_config_string_get("mame", "rom_path"), game->id);
   //~ D("DOWNLOADING URL: %s", url);
   //~ D("TO FILE: %s", buf);

   // vbox
   vbox = elm_box_add(emc->layout);
   evas_object_show(vbox);

   // label
   o = elm_label_add(emc->layout);
   elm_label_label_set(o, "Downloading ROM<br>Please wait...");
   evas_object_show(o);
   elm_box_pack_end(vbox, o);

   // progressbar
   prog = elm_progressbar_add(emc->layout);
   evas_object_size_hint_weight_set(prog, EVAS_HINT_EXPAND, 0.0);
   evas_object_size_hint_align_set(prog, EVAS_HINT_FILL, 0.0);
   evas_object_show(prog);
   elm_box_pack_end(vbox, prog);

   elm_pager_content_push(mame_info_pager, vbox);
   res = ecore_file_download(url, buf, _dwnl_rom_complete_cb,
                             _dwnl_rom_progress_cb, prog, NULL);
   if (!res)
   {
      elm_pager_content_pop(mame_info_pager);

      o = elm_label_add(emc->layout);
      elm_label_label_set(o, "Error: can not download file.");
      evas_object_show(o);

      elm_pager_content_push(mame_info_pager, o);
   }
}

static void
_artworks_clicked(void *data, Evas_Object *obj, void *event_info)
{
   D(" ");
}

static void
_info_clicked(void *data, Evas_Object *obj, void *event_info)
{
   Evas_Object *entry, *scr;
   Mame_Game *game = data;
   Emc_Strbuf *strbuf;

   D("%s", game->id);
   elm_pager_content_pop(mame_info_pager);

   entry = elm_entry_add(emc->layout);
   strbuf = _mame_history_get(game);
   elm_entry_entry_set(entry, strbuf->buf);
   emc_strbuf_free(strbuf);

   scr = elm_scroller_add(emc->layout);
   elm_scroller_content_set(scr, entry);
   
   elm_pager_content_push(mame_info_pager, scr);
}

static void
_play_clicked(void *data, Evas_Object *obj, void *event_info)
{
   Mame_Game *game = data;

   D(" ");
   _mame_run_game(game->id);
}

static void
_delete_clicked(void *data, Evas_Object *obj, void *event_info)
{
   Mame_Game *game = data;
   char buf[PATH_MAX];
   Evas_Object *lb;
   
   D(" ");
   snprintf(buf, sizeof(buf), "%s/%s.zip",
                 emc_config_string_get("mame", "rom_path"), game->id);
   // TODO ask before unlink
   ecore_file_unlink(buf);

   elm_pager_content_pop(mame_info_pager);

   lb = elm_label_add(emc->layout);
   elm_label_label_set(lb, "Game deleted.");
   evas_object_show(lb);

   elm_pager_content_push(mame_info_pager, lb);
}

static void
_stats_clicked(void *data, Evas_Object *obj, void *event_info)
{
   D(" ");
}

#define MAME_BUTTON(LB,CB)                                                     \
   bt = elm_button_add(emc->layout);                                           \
   elm_button_label_set(bt, LB);                                               \
   elm_object_style_set(bt, "enna");                                           \
   elm_object_disabled_set(bt, EINA_TRUE);                                     \
   evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);   \
   evas_object_size_hint_align_set(bt, EVAS_HINT_FILL, EVAS_HINT_FILL);        \
   evas_object_smart_callback_add(bt, "clicked", CB, game);                    \
   elm_box_pack_end(vbox, bt);                                                 \
   evas_object_show(bt);                                                       \

static void
_browser_btn_pressed_cb(void *data, Evas_Object *obj, void *event_info)
{
   Emc_Browser_Item *item = event_info;
   Mame_Game *game;
   Evas_Object *vbox, *hbox, *bt, *o;
   char buf[PATH_MAX];

   game = eina_hash_find(mame_games_hash, item->uri);
   if (!game) return;

   snprintf(buf, sizeof(buf), "%s/%s.zip",
                 emc_config_string_get("mame", "rom_path"), game->id);

   // hbox
   hbox = elm_box_add(emc->layout);
   elm_box_horizontal_set(hbox, EINA_TRUE);
   evas_object_size_hint_weight_set(hbox, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   evas_object_size_hint_align_set(hbox, EVAS_HINT_FILL, EVAS_HINT_FILL);
   evas_object_show(hbox);

   // vbox
   vbox = elm_box_add(emc->layout);
   evas_object_show(vbox);
   elm_box_pack_end(hbox, vbox);

   // buttons
   MAME_BUTTON("Info", _info_clicked);
   elm_object_disabled_set(bt, EINA_FALSE);
   MAME_BUTTON("Artworks", _artworks_clicked);
   MAME_BUTTON("Statistics", _stats_clicked);
   if (ecore_file_exists(buf))
   {
      MAME_BUTTON("Play", _play_clicked);
   }
   else
   {
      MAME_BUTTON("Download", _download_clicked);
   }
   MAME_BUTTON("Delete", _delete_clicked);

   
   // separator
   o = elm_separator_add(emc->layout);
   elm_separator_horizontal_set(o, EINA_FALSE);
   elm_box_pack_end(hbox, o);
   evas_object_show(o);

   // pager
   o = elm_pager_add(emc->layout);
   elm_object_style_set(o, "enna");
   evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
   evas_object_show(o);
   elm_box_pack_end(hbox, o);
   mame_info_pager = o;

   // start with info
   _info_clicked(game, NULL, NULL);

   // show the dialog
   emc_gui_dialog_show(game->name, hbox,
                       "default", EINA_FALSE, EINA_TRUE);
}
/*****************************************************************************/
/*                         internals                                         */
/*****************************************************************************/

static void
dwnl_snap_complete_cb(void *data, const char *file, int status)
{
   Mame_Game *game = data;

   if (mame_current_game == game)
      _update_info(game);
}

static void
_update_info(Mame_Game *game)
{
   char buf[PATH_MAX];
   char url[PATH_MAX];

   snprintf(buf, sizeof(buf), "%s/%s/0000.png",
            emc_config_string_get("mame", "snap_path"), game->id);

   if (ecore_file_exists(buf))
   {
      Evas_Object *img;

      img = elm_image_add(emc->layout);
      elm_image_file_set(img, buf, NULL);
      emc_gui_info_show(img);
   }
   else
   {
      char *dir;

      //download snap
      dir = ecore_file_dir_get(buf);
      if (!ecore_file_exists(dir))
         ecore_file_mkpath(dir);

      snprintf(url, sizeof(url),
               "http://www.progettoemma.net/snap/%s/0000.png", game->id);
      ecore_file_download(url, buf, dwnl_snap_complete_cb, NULL, game, NULL);
   }
}

static Eina_List *
_my_games_list(void)
{
   Eina_List *list = NULL;
   Eina_List *file_list;
   char *rom, *id;
   Emc_Browser_Item *item;
   Mame_Game *game;

   D(" ");
   file_list = ecore_file_ls(emc_config_string_get("mame", "rom_path"));
   EINA_LIST_FREE(file_list, rom)
   {
      id = ecore_file_strip_ext(rom);
      game = eina_hash_find(mame_games_hash, id);
      if (game)
      {
         item = emc_browser_item_new(game->id, game->name, NULL, EINA_FALSE);
         list = eina_list_append(list, item);
      }
      free(id);
      free(rom);
   }

   return list;
}

static Eina_List *
_all_games_list(void)
{
   Eina_List *list = NULL, *l;
   Emc_Browser_Item *item;
   Mame_Game *game;

   EINA_LIST_FOREACH(mame_games, l, game)
   {
      item = emc_browser_item_new(game->id, game->name, NULL, EINA_FALSE);
      list = eina_list_append(list, item);
   }
   return list;
}

static void
_browser_hilighted_cb(void *data, Evas_Object *obj, void *event_info)
{
   Emc_Browser_Item *item = event_info;
   Mame_Game *game;
   
   D("%s", item->uri);
   game = eina_hash_find(mame_games_hash, item->uri);
   
   if (game)
   {
      _update_info(game);
      mame_current_game = game;
   }
   else
   {
      emc_gui_info_hide();
   }
   
}

static void
_browser_selected_cb(void *data, Evas_Object *obj, void *event_info)
{
   Emc_Browser_Item *item = event_info;

   D("SEL!! %s", item->label);
   if (item->is_dir)
   {
      if (!strcmp(item->uri, "mame:my"))
         emc_browser_obj_push(obj, _my_games_list(), EINA_TRUE);
      else if (!strcmp(item->uri, "mame:all"))
         emc_browser_obj_push(obj, _all_games_list(), EINA_TRUE);
   }
   else
   {
      _mame_run_game(item->uri);
   }
}

static void
_mame_show(void *data)
{
   Evas_Object *brow;
   Eina_List *list = NULL;
   Emc_Browser_Item *item;

   if (!mame_games)
   {
      // build game list //TODO show a dialog while doing this
      _mame_build_list();
   }
   
   brow = emc_browser_obj_add(evas_object_evas_get(emc->layout));
   emc_browser_obj_button_set(brow, "Info", "icon/enna");
   evas_object_smart_callback_add(brow, "selected", _browser_selected_cb, NULL);
   evas_object_smart_callback_add(brow, "hilighted", _browser_hilighted_cb, NULL);
   evas_object_smart_callback_add(brow, "btn_pressed", _browser_btn_pressed_cb, NULL);

   item = emc_browser_item_new("mame:my", "My Games", NULL, EINA_TRUE);
   list = eina_list_append(list, item);

   item = emc_browser_item_new("mame:all", "All Games", NULL, EINA_TRUE);
   list = eina_list_append(list, item);

   item = emc_browser_item_new("mame:fav", "Favorite Games", NULL, EINA_TRUE);
   list = eina_list_append(list, item);

   emc_browser_obj_push(brow, list, EINA_FALSE);
   emc_gui_content_show(brow);
}

/******************************************************************************/
/*                     Module interface                                       */
/******************************************************************************/

Eina_Bool
module_init(Emc_Module *em)
{
   Evas_Object *ic;
   char buf[PATH_MAX];
   
   D(" ");


   // create default history_file in config file
   if (!emc_config_string_get("mame", "history_file"))
   {
      snprintf(buf, sizeof(buf), "%s/.mame/history.dat", getenv("HOME"));
      emc_config_string_set("mame", "history_file", buf);
   }

   // create default roms_path in config file
   if (!emc_config_string_get("mame", "rom_path"))
   {
      snprintf(buf, sizeof(buf), "%s/.mame/roms", getenv("HOME"));
      emc_config_string_set("mame", "rom_path", buf);
      ecore_file_mkpath(buf);
   }

   // get snap_path from config file
   if (!emc_config_string_get("mame", "snap_path"))
   {
      snprintf(buf, sizeof(buf), "%s/.mame/snap", getenv("HOME"));
      emc_config_string_set("mame", "snap_path", buf);
      ecore_file_mkpath(buf);
   }

   // add mainmenu item
   ic = elm_icon_add(emc->win);
   elm_icon_file_set(ic, emc->theme_file, "icon/arcade");
   emc_mainmenu_item_append("games_mame", "M.A.M.E.", ic, "games", 10,
                            _mame_show, NULL);

   return EINA_TRUE;
}

Eina_Bool
module_shutdown(Emc_Module *em)
{
   Mame_Game *game;
   
   D(" ");

   eina_hash_free(mame_games_hash);
   EINA_LIST_FREE(mame_games, game)
   {
      EMC_STRINGSHARE_DEL(game->id);
      EMC_STRINGSHARE_DEL(game->name);
      EMC_FREE(game);
   }

   emc_mainmenu_item_del("games_mame");
   return EINA_TRUE;
}

Emc_Module_Api module_api =
{
   EMC_MODULE_API_VERSION,
   EMC_MODULE_NAME,
   "M.A.M.E. Frontend",
   NULL,
   "Module to play M.A.M.E. games",
   "bla bla bla<br><b>bla bla bla</b><br><br>bla.",
   {
      module_init,
      module_shutdown
   }
};
